package com.ltu.timkor6.gamesite.games.suduku.server;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.ltu.timkor6.gamesite.shared.DoubleArray;

/**
 * Suduku based on latin squares
 * 
 * @author tim
 *
 */
public class Suduku
{
	private static final int CELL_SIZE = 9;
	private int[] rowSwap = {0, 3, 6, 1, 4, 7, 2, 5, 8};
	
	private Random random;
	private DoubleArray suduku;
	private DoubleArray game;
	
	private int numberOfGiven;
	
	public Suduku()
	{
		
	}
	
	public void generate()
	{
		this.numberOfGiven = 0;
		
		// TODO - Add seed value?
		this.random = new Random();
		
		// Setup the suduku array
		this.suduku = new DoubleArray(Suduku.CELL_SIZE, Suduku.CELL_SIZE);
		
		// Get the Latin Squares
		LatinSquares squares = new LatinSquares();
		
		// Randomize outer latin square
		int randomOuter = getRandom(0, squares.size());
		
		// Create a 3x3 permutation of the 12 Latin Squares
		List<Integer> permutation = randomPermutation(Suduku.CELL_SIZE);
		
		// Big cells 3 x 3
		for(int rowOuter = 0; rowOuter < 3; rowOuter++)
		{
			for(int columnOuter = 0; columnOuter < 3; columnOuter++)
			{
				int rowBase = rowOuter * 3;
				int columnBase = columnOuter * 3;
				
				// Randomize inner Latin Square
				int randomInner = getRandom(0, squares.size());
				
				// Get Latin Square for outer cell
				int upperDigit = squares.getCell(randomOuter, rowOuter, columnOuter) * 3;
				
				// Smaller cells 3 x 3
				for(int rowInner = 0; rowInner < 3; rowInner++)
				{
					for(int columnInner = 0; columnInner < 3; columnInner++)
					{
						// Swap rows according to Latin Squares routine
						int row = rowSwap[rowBase + rowInner];
						int column = columnBase + columnInner;
						
						// Get the Latin Square cell
						int cell = squares.getCell(randomInner, rowInner, columnInner);
						
						// Translate a value for the square
						int value = permutation.get(cell + upperDigit) + 1;
						
						// Set the value to the Suduku
						this.suduku.set(row, column, value);
					}
				}
			}
		}
	}
	
	public void createGame()
	{
		createGame(40);
	}
	
	/**
	 * Creates a Suduku game with 40 preset numbers.
	 */
	public void createGame(int numbers)
	{
		this.game = new DoubleArray(Suduku.CELL_SIZE, Suduku.CELL_SIZE);
		
		// Pick 40 numbers and seed them on to a blank field
		while(this.numberOfGiven < numbers)
		{
			int row = getRandom(0, 9);
			int column = getRandom(0, 9);
			
			if(this.game.get(row, column) == 0)
			{
				int value = this.suduku.get(row, column);
				
				this.game.set(row, column, value);
				
				this.numberOfGiven++;
			}
		}
	}
	
	public boolean equal(List<List<Integer>> list)
	{
		return (list.equals(this.suduku.get()));
	}
	
	/*
	private boolean validate()
	{
		return false;
	}
	*/
	
	public void backtrace()
	{
		
	}
	
	/**
	 * Generate a random list of 0 to n Integers from a k big list where n < k.
	 * @param n
	 * @return
	 */
	private List<Integer> randomPermutation(int n)
	{
		List<Integer> list = new ArrayList<Integer>();
		
		// Create a list from 0 to n
		for(int i = 0; i < n; i++)
		{
			list.add(i);
		}
				
		// Swap numbers around.
		for(int i = 0; i < n; i++)
		{
			int randomNumber = getRandom(0, n);
			
			int temp = list.get(randomNumber);
			int swap = list.get(i);
			
			list.set(randomNumber, swap);
			list.set(i, temp);
		}
		
		return list;
	}
	
	/**
	 * Generate a random number between lower up to upper.
	 * @param lower
	 * @param upper
	 * @return
	 */
	private int getRandom(int lower, int upper)
	{
		int numbers = random.nextInt(upper-lower);
		
		return numbers + lower;
	}
	
	public List<List<Integer>> getSuduku()
	{
		return this.suduku.get();
	}
	
	public List<List<Integer>> getGame()
	{
		return this.game.get();
	}
	
	public int getGiven()
	{
		return this.numberOfGiven;
	}
	
	/**
	 * Print a DubbleArray with rows and cols.
	 */
	public void print()
	{
		System.out.println("Suduku of size " + this.suduku.getRowSize() + "x" + this.suduku.getColSize());
		
		for(int row = 0; row < this.suduku.getRowSize(); row++)
		{
			for(int col = 0; col < this.suduku.getColSize(); col++)
			{
				System.out.print(this.suduku.get(row, col) + " ");
			}
			
			System.out.println("");
		}
	}
}
